<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>

<body>
    <canvas id="canvas"></canvas>
</body>
<!-- Line -->
<script id="TextureVertexShader" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    attribute vec2 a_Pin;
    uniform float u_PointSize;
    uniform mat4 u_ModelM4;
    uniform mat4 u_aM4;
    uniform mat4 u_PvM4;
    varying vec2 v_Pin;
    void main(){
        gl_Position = u_PvM4 * u_ModelM4 * a_Position;
        gl_PointSize = u_PointSize;
        v_Pin = a_Pin;
    }
</script>
<script id="TextureFragmentShader" type="x-shader/x-fragment">
    precision mediump float;
    uniform sampler2D u_Sampler;
    varying vec2 v_Pin;
    void main(){
        gl_FragColor = texture2D(u_Sampler, v_Pin);
    }
</script>
<script id="PointVertexShader" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    uniform float u_PointSize;
    uniform mat4 u_ModelM4;
    uniform mat4 u_PvM4;
    void main(){
        gl_Position = u_PvM4 * u_ModelM4 * a_Position;
        gl_PointSize = u_PointSize;
    }
</script>
<script id="PointFragmentShader" type="x-shader/x-fragment">
    precision mediump float;
    void main(){
        gl_FragColor = vec4(1,1,1,1);
    }
</script>
<script type="module">
    import { Matrix4, Vector3, Color, PerspectiveCamera, Spherical } from '../js/three.module.js';
    import { initShaders, CreateProgram } from '../js/Utils.js';
    import lib from '../js/ThreeLib.js';
    import CameraController from '../js/CameraController.js'
    import Geo from '../js/Poly/Geo.js'
    import Mat from '../js/Poly/Mat.js'
    import Obj3D from '../js/Poly/Obj3D.js'
    import Scene from '../js/Poly/Scene.js'



    //#region gl
    const canvas = document.getElementById("canvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight - 4;

    const gl = canvas.getContext("webgl");
    //开始背景遮挡
    // gl.enable(gl.DEPTH_TEST);
    // gl.enable(gl.BLEND);
    // gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    //#endregion

    //创建程序对象
    let textureProgram = CreateProgram(gl,
        document.querySelector('#TextureVertexShader').innerText,
        document.querySelector('#TextureFragmentShader').innerText);
    let pointProgram = CreateProgram(gl,
        document.querySelector('#PointVertexShader').innerText,
        document.querySelector('#PointFragmentShader').innerText);

    const scene = new Scene({ gl: gl });
    const pvM4 = new Matrix4();
    const modelM4 = new Matrix4(
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
    );
    const pointSize = 15;


    let [imageX, imageY] = [0.3, 1];

    const textureElementArr = [
        0, 1, 2,
        0, 2, 3,
    ];
    const pointElementArr = [
        0, 1,
        1, 2,
        2, 3,
        3, 0,
    ];
    const pinArr = [
        0, 1,
        0, 0,
        1, 0,
        1, 1,
    ];



    let img = new Image();
    img.src = './Image/Image01.jpg';
    imageY = imageX / (img.width / img.height);
    imageY = imageY * (canvas.width / canvas.height);//这个地方应该是显示元素的比值

    const nodeArrBase = [
        -imageX, imageY,
        -imageX, -imageY,
        imageX, -imageY,
        imageX, imageY,
    ];
    const nodeArr = nodeArrBase.concat();

    img.onload = function () {

        let textureObj3D = new Obj3D({
            geo: new Geo({
                data: {
                    'a_Position': { size: 2, array: new Float32Array(nodeArr) },
                    'a_Pin': { size: 2, array: new Float32Array(pinArr) },
                },
                element: {
                    array: new Uint8Array(textureElementArr)
                }
            }),
            mat: new Mat({
                program: textureProgram,
                mode: ['TRIANGLES'],
                data: {
                    'u_PvM4': { type: 'uniformMatrix4fv', value: pvM4.elements },
                    'u_ModelM4': { type: 'uniformMatrix4fv', value: modelM4.elements },
                },
                maps: {
                    'u_Sampler': { image: img, WRAP_W: gl.LINEAR, WRAP_H: gl.LINEAR, }
                }
            }),
        });
        let pointObj3D = new Obj3D({
            geo: new Geo({
                data: {
                    'a_Position': { size: 2, array: new Float32Array(nodeArr) },
                },
                element: {
                    array: new Uint8Array(pointElementArr)
                }
            }),
            mat: new Mat({
                program: pointProgram,
                mode: ['POINTS', 'LINES'],
                data: {
                    'u_PvM4': { type: 'uniformMatrix4fv', value: pvM4.elements },
                    'u_ModelM4': { type: 'uniformMatrix4fv', value: modelM4.elements },
                    'u_PointSize': { type: 'uniform1f', value: pointSize },
                },
            }),
        });

        scene.add(pointObj3D);
        scene.add(textureObj3D);
        draw();

    };

    let change = false;
    let start = 0;
    //0:默认
    //1:位移
    const cursorArr = ['default', 'move', 'pointer', 'nw-resize', 'ne-resize'];
    let dragStart = new Vector3();//拖拽起始位(世界坐标)
    let dragEnd = new Vector3();//拖拽结束位(世界坐标)
    let start2Orgin = new Vector3();//拖拽起始点.sub(基点)
    let end2Orgin = new Vector3();//拖拽结束点.sub(基点)
    let endAng = 0;//结束弧度
    let angle = 0;//弧度,用于旋转
    let offset = new Vector3();//偏移量,用于移动
    let resize = new Vector3(1, 1, 1);//缩放大小,用于缩放
    let nodeIndex = 0;//当前点下标
    let orgin = new Vector3();//基点
    let keyCode = 0;

    const mi = new Matrix4();
    const mb = new Matrix4();

    const mh = new Matrix4();

    canvas.addEventListener('mousemove', function (e) {
        const { clientX, clientY } = e;
        let curPoint = CssToGl(clientX, clientY);

        // if (start == 0) {
        let cursor = cursorArr[0];
        if (isInTexture(curPoint)) {
            cursor = cursorArr[1];
        } else if (isNodeAround(curPoint, pointSize)) {
            cursor = cursorArr[2];
        } else if (isNodeAround(curPoint, pointSize * 2)) {
            if (nodeIndex % 2 == 0)
                cursor = cursorArr[3];
            else {
                cursor = cursorArr[4];
            }
        }
        canvas.style.cursor = cursor;
        // }

        //移动
        dragEnd.copy(curPoint);
        //旋转
        end2Orgin = new Vector3().subVectors(dragEnd, orgin);
        //缩放
        change = true;
        switch (start) {
            case 1:
                move();
                break;
            case 2:
                rotate();
                break;
            case 3:
                scale();
                break;
        }
        draw();

    });

    canvas.addEventListener('mousedown', function (e) {
        const { clientX, clientY } = e;
        const cur = CssToGl(clientX, clientY);

        if (isInTexture(cur)) {//在图片里
            start = 1;
            dragStart.copy(cur);
        } else if (isNodeAround(cur, pointSize)) {//在点周围
            start = 2;
            dragStart.copy(cur);
            rotate();
        } else if (isNodeAround(cur, pointSize * 2)) {
            start = 3;
            dragStart.copy(cur);
            scale();
        }
    });

    canvas.addEventListener('mouseup', function (e) {
        const { clientX, clientY } = e;
        start = 0;
        if (change) {
            change = false;
            offset.set(0, 0, 0);
            angle = 0;
            resize.set(1, 1, 1);
            formatVertices();
        }
    });

    window.addEventListener('keydown', function (e) {
        setKeyCode(e.keyCode);
    });
    window.addEventListener('keyup', function () {
        setKeyCode(0);
    });


    //移动
    function move() {
        offset.copy(dragStart.clone().sub(dragEnd));
    }
    //旋转
    function rotate() {
        //设置基点
        if (keyCode == 17) {
            setOrgin(nodeIndex);
        } else {
            setOrgin(-1);
        }
        let startAng = Math.atan2(start2Orgin.y, start2Orgin.x)
        let endAng = Math.atan2(end2Orgin.y, end2Orgin.x);
        angle = endAng - startAng;
    }
    //缩放
    function scale() {
        //设置基点
        if (keyCode == 17) {
            setOrgin(nodeIndex);
        } else {
            setOrgin(-1);
        }
        if (keyCode == 16) {
            const sx = end2Orgin.x / start2Orgin.x;
            const sy = end2Orgin.y / start2Orgin.y;
            resize.set(sx, sy, 0);

        } else {
            const ratio = end2Orgin.length() / start2Orgin.length();
            // resize.set(ratio * sx / Math.abs(sx), ratio * sy / Math.abs(sy), 0);
            resize.set(ratio, ratio, 0);
        }
        console.log(start2Orgin, end2Orgin)
    }

    function setKeyCode(num) {
        if (num == keyCode)
            return;
        keyCode = num;

        switch (start) {
            case 1:
                break;
            case 2:
                if (keyCode == 17) {
                    setOrgin(nodeIndex);
                } else {
                    setOrgin(-1);
                }
                end2Orgin = new Vector3().subVectors(dragEnd, orgin);
                rotate();
            case 3:
                //重新设置基点
                if (keyCode == 17) {
                    setOrgin(nodeIndex);
                } else {
                    setOrgin(-1);
                }
                end2Orgin = new Vector3().subVectors(dragEnd, orgin);
                scale();
                break;
        }
        draw();

    }

    //判断鼠标是否在Texture中
    function isInTexture(node) {
        if (lib.inTriangle(node, [new Vector3(nodeArr[0], nodeArr[1], 0), new Vector3(nodeArr[2], nodeArr[3], 0), new Vector3(nodeArr[4], nodeArr[5], 0)]) ||
            lib.inTriangle(node, [new Vector3(nodeArr[0], nodeArr[1], 0), new Vector3(nodeArr[4], nodeArr[5], 0), new Vector3(nodeArr[6], nodeArr[7], 0)])) {
            return true;
        }
        return false;
    }

    //判断鼠标是否在点周围
    function isNodeAround(node, around) {
        const { x, y } = node;
        let curNode = null;
        let [a, b] = CssToGlOfDiff(around, around);
        for (let i = 0; i < nodeArr.length; i += 2) {
            if (x > nodeArr[i] - a && x < nodeArr[i] + a &&
                y > nodeArr[i + 1] - a && y < nodeArr[i + 1] + a) {
                nodeIndex = i / 2;
                return true;
            }
        }
        return false;
    }

    //Css转GL
    function CssToGl(x, y) {
        let hW = canvas.width / 2;
        let hH = canvas.height / 2;
        return new Vector3((x - hW) / hW, -1 * (y - hH) / hH, 0);
    }
    function CssToGlOfDiff(x, y) {
        return [x / (canvas.width / 2), y / (canvas.height / 2)]
    }

    //获取中心点
    function getCenter() {
        let [x1, y1] = [nodeArr[0], nodeArr[1]];
        let [x2, y2] = [nodeArr[4], nodeArr[5]];
        // return new Vector3(
        //     x1 + (x2 - x1) / 2,
        //     y1 + (y2 - y1) / 2,
        //     0
        // );
        return new Vector3(
            (x2 + x1) / 2,
            (y2 + y1) / 2,
            0
        );
    }
    //获取基点根据下标对点
    function getOrgin(index) {
        if (index > 1) {
            index -= 2;
        } else {
            index += 2;
        }
        return new Vector3(
            nodeArr[index * 2],
            nodeArr[index * 2 + 1],
            0,
        );
    }

    // 设置基点
    function setOrgin(num = -1) {
        if (num == -1) {
            orgin = getCenter();
        } else {
            orgin = getOrgin(num);
        }
        start2Orgin = new Vector3().subVectors(dragStart, orgin);
        let { x, y, z } = orgin;
        mi.setPosition(-x, -y, z);
        mb.setPosition(x, y, z);
    }

    //全部变换
    function formatVertices() {
        mh.multiplyMatrices(modelM4, mh);
        for (let i = 0; i < nodeArr.length; i += 2) {
            const p = new Vector3(nodeArrBase[i], nodeArrBase[i + 1], 0).applyMatrix4(mh);
            nodeArr[i] = p.x;
            nodeArr[i + 1] = p.y;
        }
        // scene.setAttrib('a_Position', { array: new Float32Array(nodeArr) });
        // scene.setUniform('u_ModelM4', { value: mh.elements });
        // draw();
    }

    //从偏移量获取模型矩阵
    function getModelMatrix() {
        //获取位移矩阵
        const { x: px, y: py } = offset
        // const moveMatrix = new Matrix4().set(
        //     1, 0, 0, -px,
        //     0, 1, 0, -py,
        //     0, 0, 1, 0,
        //     0, 0, 0, 1,
        // );
        const moveMatrix = new Matrix4().makeTranslation(-px, -py, 0);
        // const rotateMatrix = new Matrix4().set(
        //     cos(angle), -sin(angle), 0, 0,
        //     sin(angle), cos(angle), 0, 0,
        //     0, 0, 1, 0,
        //     0, 0, 0, 1,
        // );
        const rotateMatrix = new Matrix4().makeRotationZ(angle);
        const scaleMatrix = new Matrix4().set(
            resize.x, 0, 0, 0,
            0, resize.y, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1,
        );
        // const scaleMatrix = new Matrix4().makeScale(resize.x, resize.y, 1);
        // 模型矩阵
        return mb.clone()
            .multiply(moveMatrix)
            .multiply(rotateMatrix)
            .multiply(scaleMatrix)
            .multiply(mi)
    }


    //渲染(根据偏移量进行渲染的,模型矩阵又根据偏移量获得)
    function draw() {
        const { elements } = modelM4.copy(getModelMatrix()).clone().multiply(mh);
        scene.setUniform('u_ModelM4', { value: elements });
        scene.draw();
    }


</script>

</html>